﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Farigola.Engine.SystemData.Entity.Type;
using Farigola.Engine.SystemData.Entity;
using Farigola.Engine.SystemData.Entity.inst;
using Farigola.Engine.SystemData.DataValue;

namespace Farigola.Engine.Factories
{
    /// <summary>
    /// The factory to build entities, types and instances.
    /// </summary>
    public class FactoryEntity
    {
        /// <summary>
        /// Need the common factory.
        /// </summary>
        private FactoryCommon _factoryCommon;

        //=====================================================================
        #region Constructors.

        //---------------------------------------------------------------------
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="factoryCommon"></param>
        public FactoryEntity(FactoryCommon factoryCommon)
        {
            _factoryCommon = factoryCommon;
        }

        #endregion

        //=====================================================================
        #region get/set methods.

        #endregion

        //====================================================================
        #region Convert public methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// Convert a string to TypeSystOrUser enum value.
        /// </summary>
        /// <param name="systOrUserStr"></param>
        /// <param name="systOrUserStr"></param>
        /// <returns></returns>
        public bool ConvertStringToSystOrUser(string systOrUserStr, out EntityDef.TypeSystOrUser systOrUser)
        {
            systOrUser = EntityDef.TypeSystOrUser.User;
            if (systOrUserStr == null)
                return false;

            if (systOrUserStr.Trim().Equals(EntityDef.TypeSystOrUser.User.ToString(),StringComparison.OrdinalIgnoreCase))
            {
                systOrUser = EntityDef.TypeSystOrUser.User;
                return true;
            }

            if (systOrUserStr.Trim().Equals(EntityDef.TypeSystOrUser.System.ToString(),StringComparison.OrdinalIgnoreCase))
            {
                systOrUser = EntityDef.TypeSystOrUser.System;
                return true;
            }

            return false;
        }

        #endregion

        //====================================================================
        #region create project entity methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// Create the root folder of the project: 
	    /// $$$prj_root, 
	    ///     $$$prj_root_system, 
        ///     $$$prj_root_user
        ///
        /// </summary>
        /// <returns></returns>
        public EntInstFolder CreateAllRootFolderProject(){
				
		    // create the 3 root folders
            EntInstFolder rootFolder = CreateEntInstFolder(EntityDef.TypeSystOrUser.System, _factoryCommon.CreateEntityId(), EntityDef.prjRootFolderName);
            EntInstFolder rootSystemFolder = CreateEntInstFolder(EntityDef.TypeSystOrUser.System, _factoryCommon.CreateEntityId(), EntityDef.prjRootSystemFolderName);
            EntInstFolder rootUserFolder = CreateEntInstFolder(EntityDef.TypeSystOrUser.System, _factoryCommon.CreateEntityId(), EntityDef.prjRootUserFolderName);
		
		    // link folders, parent->child
		    rootFolder.AddChild(rootSystemFolder);
		    rootSystemFolder.SetParentFolder(rootFolder);
		
		    rootFolder.AddChild(rootUserFolder);
		    rootUserFolder.SetParentFolder(rootFolder);
		
		    return rootFolder;
	    }

        #endregion

        //====================================================================
        #region create entity types class methods.

        //---------------------------------------------------------------------
	    public EntTypeClass CreateEntTypeClass(EntityDef.TypeSystOrUser systOrUser, EntityId id, String name){
            // check that id is valid
    	    if(!EntityId.Check(id))
    		    return null;
		
    	    // check the name
    	    if(!_factoryCommon.checkSyntaxName(name))
    		    return null;

    	    return new EntTypeClass(systOrUser, id, name);
	    }
	
        //---------------------------------------------------------------------
	    public EntTypeClassAnonym CreateEntTypeClassAnonym(EntityDef.TypeSystOrUser systOrUser, EntityId id, String name)
        {
            // check that id is valid
    	    if(!EntityId.Check(id))
    		    return null;
		
    	    // check the name
    	    if(!_factoryCommon.checkSyntaxName(name))
    		    return null;

    	    return new EntTypeClassAnonym(systOrUser, id, name);
	    }
	
        //---------------------------------------------------------------------
        /// <summary>
        /// create an entity type class; std.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntTypeClass CreateEntTypeClass(String name)
        {
    	    // create a new id
    	    EntityId id= _factoryCommon.CreateEntityId();

    	    return  CreateEntTypeClass(EntityDef.TypeSystOrUser.User, id, name); 
        }
    
        //---------------------------------------------------------------------
        /// <summary>
        /// create an entity type class; anonym.
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntTypeClassAnonym CreateEntTypeClassAnonym(String name)
        {
    	    // create a new id
    	    EntityId id= _factoryCommon.CreateEntityId();

    	    return  CreateEntTypeClassAnonym(EntityDef.TypeSystOrUser.User, id, name); 
        }
    
    
        //---------------------------------------------------------------------
        /// <summary>
        /// create an entity type class (std or anonym).
        /// </summary>
        /// <param name="typeClassBaseName"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntTypeClassBase CreateEntTypeClassBase(String typeClassBaseName, String name)
        {
    	    if(typeClassBaseName == null)
    		    return null;
    	
    	    if(typeClassBaseName.Equals(typeof(EntTypeClass).ToString(), StringComparison.OrdinalIgnoreCase))
    		    return CreateEntTypeClass(name);
    	
    	    if(typeClassBaseName.Equals(typeof(EntTypeClassAnonym).ToString(), StringComparison.OrdinalIgnoreCase))
    		    return CreateEntTypeClassAnonym(name);
    	
    	    return null;
        }
	
    
        //---------------------------------------------------------------------
        /// <summary>
        /// create a data member on the type class, base on a type, set the name.
        /// </summary>
        /// <param name="typeDM"></param>
        /// <param name="nameDM"></param>
        /// <returns></returns>
        public EntTypeClassDataMember CreateEntTypeClassDataMember(EntTypeBase typeDM, String nameDM)
        {
    	    // check the type of DM
    	    // todo: is possible?

    	    // check the name
    	    if(!_factoryCommon.checkSyntaxName(nameDM))
    		    return null;
    	
    	    // create a new id
    	    EntityId id= _factoryCommon.CreateEntityId();
    	
    	    return new EntTypeClassDataMember(id, nameDM, typeDM);
        }
        #endregion

        //====================================================================
        #region specific methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// Create a new name for a type class anonym, used the name of the inst class. 
        /// </summary>
        /// <param name="instName"></param>
        /// <returns></returns>
        private string CreateNewNameEntTypeClassAnonym(String instName){
    	    String s="";
    	    if(instName!=null)
    		    s=instName + "_";
    	
		    // todo: create a constant for this value
    	    return "typeClassAnonym_"+ s + _factoryCommon.CreateEntityId().ToString();    	
        }
        #endregion

        //====================================================================
        #region create entity instance folder methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// Create a new folder instance entity.
        ///  its a user entity (not system).
        /// </summary>
        /// <param name="systOrUser"></param>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstFolder CreateEntInstFolder(EntityDef.TypeSystOrUser systOrUser, EntityId id, String name)
        {
            // check that id is valid
    	    if(!EntityId.Check(id))
    		    return null;
    	
    	    // check the name
    	    if(!_factoryCommon.checkSyntaxName(name))
    		    return null;
    	
            return new EntInstFolder(systOrUser, id, name);
        }


        //---------------------------------------------------------------------
        /// <summary>
        /// Create a new folder instance entity.
        /// its a user entity (not system).
        /// </summary>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstFolder CreateEntInstFolder(EntityId id, String name)
        {
    	    return CreateEntInstFolder(EntityDef.TypeSystOrUser.User, id, name);
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// Create a new folder instance entity.
        /// its a user entity (not system).
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstFolder CreateEntInstFolder(String name)
        {
    	    // create a new id
    	    EntityId id= _factoryCommon.CreateEntityId();

            return CreateEntInstFolder(id, name);
        }
        #endregion

        //====================================================================
        #region create entity instance basic type methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// Create a new folder instance string (its a basic type instance).
        ///  
        /// low-level API method.
        /// </summary>
        /// <param name="systOrUser"></param>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstString CreateEntInstString(EntityDef.TypeSystOrUser systOrUser, EntityId id, String name){
            // check that id is valid
    	    if(!EntityId.Check(id))
    		    return null;

    	    // check the name
    	    if(!_factoryCommon.checkSyntaxName(name))
    		    return null;

            return new EntInstString(systOrUser, id, name);
	    }

        //---------------------------------------------------------------------
        /// <summary>
        /// Create an entity instance , a basic type (string, int,...), depending on provided type.
        /// </summary>
        /// <param name="entType"></param>
        /// <param name="systOrUser"></param>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstBasicBase CreateEntInstBasicBase(EntTypeBase entType, EntityDef.TypeSystOrUser systOrUser, EntityId id, String name){
		    // is the type valid
		    if(entType == null)
			    return null;
		
		    // is the type an entity folder?
		    // -> no, only inst basic
		    //if(_factoryCommon.isEntTypeFolder(entType))
		    //{
			    // so create an entity instance folder
		    //	return createEntInstFolder(systOrUser, id, name);
		    //}
		
		    // is the type a string?
		    if(_factoryCommon.IsEntTypeString(entType))
		    {
			    return CreateEntInstString(systOrUser, id, name);			
		    }
		
		    // is the type an int?
		    // todo:
		
		    //unknown or not yet implemented
		    return null;
	    }
	
        //---------------------------------------------------------------------
        /// <summary>
        /// Create an entity instance, depending on provided type.
	    /// By default, its a user instance, generate an id.
        /// </summary>
        /// <param name="entType"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstBasicBase CreateEntInstBasicBase(EntTypeBase entType, String name){
    	    // create a new id .
    	    EntityId id= _factoryCommon.CreateEntityId();

		    return  CreateEntInstBasicBase(entType, EntityDef.TypeSystOrUser.User, id, name);		
	    }
        #endregion

        //====================================================================
        #region create entity instance DM class methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// Create the DM inst of the inst class.
	    /// (not attach the DM inst under the inst class, just created).
        /// </summary>
        /// <param name="instClass"></param>
        /// <param name="typeClassDM"></param>
        /// <returns></returns>
        public EntInstBase CreateInstClassDataMember(EntInstClassBase instClass, EntTypeClassDataMember typeClassDM){
		    // get the type of the data member
		    EntTypeBase entTypeBaseDM = typeClassDM.GetEntTypeBase();
		
		    // the type of the DM is a basic type (string, int,...)?		
		    if(_factoryCommon.IsEntTypeBasic(entTypeBaseDM))
			    return CreateEntInstBasicBase(entTypeBaseDM, typeClassDM.GetName());
			    //return createInstClassDataMemberBasicType(instClass, (EntTypeBasicBase)entTypeBaseDM);
		
		    // todo: other types
		    // todo: createInstClassDataMemberClass (std or anonym)
		    // ...
		
		    return null;
	    }

        #endregion

        //====================================================================
        #region attach inst DM to entity instance class methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// attach inst DM to the entity instance class.
        /// </summary>
        /// <param name="instClass"></param>
        /// <param name="typeClassDM"></param>
        /// <param name="instDM"></param>
        /// <returns></returns>
        public bool AttachEntInstDataMemberToInstClass(EntInstClass instClass, EntTypeClassDataMember typeClassDM, EntInstBase instDM){
		    // check params
		    if(instClass==null)
			    return false;
		    if(typeClassDM==null)
			    return false;
		    if(instDM==null)
			    return false;
		
		    // attach the inst DM to the inst class 
		    return instClass.addEntInstDataMember(instDM, typeClassDM);
	    }
        #endregion


        //====================================================================
        #region create entity instance class methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// Create a new inst class std.
	    /// the class is empty, no data members created here.

        /// </summary>
        /// <param name="systOrUser"></param>
        /// <param name="entType"></param>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstClass CreateEntInstClassEmpty(EntityDef.TypeSystOrUser systOrUser, EntTypeBase entType, EntityId id, String name)
        {
		    // check the type
		    if(entType==null)
			    return null;
		
		    // the type must a type class
		    if(!_factoryCommon.IsEntTypeClass(entType))
			    return null;
		
		    // check that id is valid
    	    if(!EntityId.Check(id))
    		    return null;

    	    // check the name
    	    if(!_factoryCommon.checkSyntaxName(name))
    		    return null;
		
    	    // create the ent inst class, set the type class
    	    EntInstClass instClass = new EntInstClass(systOrUser, (EntTypeClass)entType, id, name);
    	
    	    // return the created empty inst class 
    	    return instClass;
	    }

        //---------------------------------------------------------------------
        /// <summary>
        /// Create an ent inst class (std).
	    /// the inst class created is empty, with no inst DM.
        /// </summary>
        /// <param name="entType"></param>
        /// <param name="systOrUser"></param>
        /// <param name="id"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstClass CreateEntInstClassEmpty(EntTypeBase entType, EntityDef.TypeSystOrUser systOrUser, EntityId id, String name)
        {
		    // is the type a class (std)?
		    if(!_factoryCommon.IsEntTypeClass(entType))
			    return null;
		
		    return CreateEntInstClassEmpty(EntityDef.TypeSystOrUser.User,  entType, id, name);	
	    }
	
        //---------------------------------------------------------------------
	    public EntInstClassAnonym CreateEntInstClassAnonymEmpty(EntityDef.TypeSystOrUser systOrUser, EntityId id, String name)
        {
		    // check that id is valid
    	    if(!EntityId.Check(id))
    		    return null;

    	    // check the name
    	    if(!_factoryCommon.checkSyntaxName(name))
    		    return null;
		
    	    // create a specific type for the inst class anonym
    	    EntTypeClassAnonym typeClassAnonym= new EntTypeClassAnonym(systOrUser, _factoryCommon.CreateEntityId(), CreateNewNameEntTypeClassAnonym(name));
    	
    	    // create the ent inst class, set the type class
    	    EntInstClassAnonym instClass = new EntInstClassAnonym(systOrUser, typeClassAnonym, id, name);
    	
    	    // return the created empty inst class 
    	    return instClass;
	    }

        //---------------------------------------------------------------------
        /// <summary>
        /// Create an entity instance type class (std).
	    /// the inst class created is empty, with no inst DM.
        ///
        /// </summary>
        /// <param name="systOrUser"></param>
        /// <param name="entType"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstClass CreateEntInstClassEmpty(EntityDef.TypeSystOrUser systOrUser, EntTypeBase entType, String name)
        {
    	    // create a new id.
    	    EntityId id= _factoryCommon.CreateEntityId();

		    return  CreateEntInstClassEmpty(entType, systOrUser, id, name);				
	    }

        //---------------------------------------------------------------------
        /// <summary>
        /// create the entity instance class anonym.
        /// </summary>
        /// <param name="systOrUser"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstClassAnonym CreateEntInstClassAnonymEmpty(EntityDef.TypeSystOrUser systOrUser, String name)
        {
    	    // create a new id.
    	    EntityId id= _factoryCommon.CreateEntityId();

		    return  CreateEntInstClassAnonymEmpty(systOrUser, id, name);
        }

        #endregion

        //====================================================================
        #region create entity instance structure (list,...) methods.

        //---------------------------------------------------------------------
        /// <summary>
        /// create an entity instance list of items.
        /// </summary>
        /// <param name="systOrUser"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstStructList CreateEntInstStructList(EntityDef.TypeSystOrUser systOrUser, EntityId id, DataValueType dataValueType, String name)
        {
            // check that id is valid
            if (!EntityId.Check(id))
                return null;

            // check the type
            if (dataValueType == null)
                return null;
            if (dataValueType == DataValueType.Undefined)
                return null;

            // check the name
            if (!_factoryCommon.checkSyntaxName(name))
                return null;

            // create the ent inst class, set the type class
            EntInstStructList instStructList = new EntInstStructList(systOrUser, dataValueType, id, name);

            // return the created empty inst class 
            return instStructList;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// create an entity instance list of items.
        /// </summary>
        /// <param name="systOrUser"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstStructList CreateEntInstStructList(EntityDef.TypeSystOrUser systOrUser, EntityId id, EntTypeBase entTypeItems, String name)
        {
            // check that id is valid
            if (!EntityId.Check(id))
                return null;

            // check the type
            if (entTypeItems == null)
                return null;

            // the type must a type basic
            if (!_factoryCommon.IsEntTypeBasic(entTypeItems))
                return null;

            // check the name
            if (!_factoryCommon.checkSyntaxName(name))
                return null;

            // create the ent inst class, set the type class
            EntInstStructList instStructList = new EntInstStructList(systOrUser, entTypeItems, id, name);

            // return the created empty inst class 
            return instStructList;
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// create an entity instance list of items.
        /// </summary>
        /// <param name="systOrUser"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstStructList CreateEntInstStructList(EntityDef.TypeSystOrUser systOrUser, DataValueType dataValueType, String name)
        {
            // create a new id.
            EntityId id = _factoryCommon.CreateEntityId();

            return CreateEntInstStructList(systOrUser, id, dataValueType, name);
        }

        //---------------------------------------------------------------------
        /// <summary>
        /// create an entity instance list of items.
        /// </summary>
        /// <param name="systOrUser"></param>
        /// <param name="name"></param>
        /// <returns></returns>
        public EntInstStructList CreateEntInstStructList(EntityDef.TypeSystOrUser systOrUser, EntTypeBase entTypeItems, String name)
        {
            // create a new id.
            EntityId id = _factoryCommon.CreateEntityId();

            return CreateEntInstStructList(systOrUser, id, entTypeItems, name);
        }

        #endregion
    }
}
